package com.easy.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CreateCache;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easy.common.constant.RegularExpression;
import com.easy.common.core.R;
import com.easy.common.core.domain.entity.EmailBody;
import com.easy.common.core.domain.entity.User;
import com.easy.common.core.domain.model.LoginBody;
import com.easy.common.utils.AccountTypeCheckUtils;
import com.easy.common.utils.encryption.PasswordEncryptionUtils;
import com.easy.common.utils.ip.AddressUtils;
import com.easy.common.utils.ip.IpOrAddress;
import com.easy.common.utils.mail.MailUtils;
import com.easy.common.mq.MessageRocketProducer;
import com.easy.common.utils.uuid.IdUtils;
import com.easy.common.utils.uuid.SnowFlake;
import com.easy.common.utils.uuid.UUID;
import com.easy.user.mapper.UserLoginMapper;
import com.easy.user.pojo.Membership;
import com.easy.user.service.IMembershipService;
import com.easy.user.service.LoginInfoService;
import com.easy.user.service.UserLogService;
import com.easy.user.service.UserLoginService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.easy.common.constant.ResultConstant.VERIFICATION_CODE_ERROR;
import static com.easy.common.constant.SystemConstants.USER_NICK_NAME_PREFIX;
import static com.easy.common.utils.RedisConstants.LOGIN_USER_KEY;
import static com.easy.common.utils.RedisConstants.LOGIN_USER_TTL;

/**
 * 用户登录校验
 *
 * @author RuoYu
 * @date 2022/09/26
 */
@Service
public class UserLoginServiceImpl extends ServiceImpl<UserLoginMapper, User> implements UserLoginService {

    private static final Logger logger = LoggerFactory.getLogger(AddressUtils.class);

    @Autowired
    private UserLoginMapper userLoginMapper;

    /**
     * 用户 操作日志 服务
     */
    @Autowired
    private UserLogService userLogService;

    /*验证码3分钟有效*/
    @CreateCache(area = "email", name = "ord_user_", expire = 300, timeUnit = TimeUnit.SECONDS)
    private Cache<String, String> jetCache;

    /*登录错误 5 分钟*/
    @CreateCache(area = "email", name = "ord_user_error", expire = 500, timeUnit = TimeUnit.SECONDS)
    private Cache<String, String> loginErrorCache;

    /*登录用户*/
    @CreateCache(area = "userLogin", name = "try", expire = 500, timeUnit = TimeUnit.SECONDS)
    private Cache<String, User> login;

    /*token*/
    @Resource
    private StringRedisTemplate StringRedisTemplate;

    @Autowired
    private MailUtils mailUtils;

    @Autowired
    private IMembershipService membershipService;

    @Autowired
    private LoginInfoService loginInfoService;
    @Autowired
    private MessageRocketProducer messageRocketProducer;

    /**
     * 账户登录
     *
     * @param loginBody 用户登录账户信息
     * @param password  密码
     * @return {@link R}
     */
    @Override
    public String accountLogin(LoginBody loginBody, String password) {

        //1、登录账户
        String account = loginBody.getAccount();

        logger.info("account:{},password:{}", account, password);

        //手机号登录
        if (account.matches(RegularExpression.PHONE_PATTERN)) {
            logger.info("手机号登录");
            return accountType("phone", account, password);
        }

        //邮箱登录
        if (account.matches(RegularExpression.MAIL_PATTERN)) {
            logger.info("邮箱登录");
            return accountType("email", account, password);
        }

        //用户名登录
        if (account.matches(RegularExpression.USER_NAME_PATTERN)) {
            logger.info("用户名登录");
            return accountType("userName", account, password);
        }
        return "系统繁忙！";
    }

    @Transactional
    public String accountType(String type, String account, String password) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();

        //用户名
        if (type.equals("userName")) {
            lqw.eq(StringUtils.isNoneEmpty(account), User::getUserName, account);
        }

        //邮箱
        if (type.equals("email")) {
            lqw.eq(StringUtils.isNoneEmpty(account), User::getEmail, account);
        }

        //手机号
        if (type.equals("phone")) {
            lqw.eq(StringUtils.isNoneEmpty(account), User::getPhone, account);
        }

        //指定查询字段，防止信息泄露（id、userName、Email、password、phone、status）
//        lqw.select(OrdUser::getId, OrdUser::getUserName, OrdUser::getEmail, OrdUser::getPassword, OrdUser::getPhone, OrdUser::getStatus);

        //1、查询登录用户信息
        User user = userLoginMapper.selectOne(lqw);
        if (user == null) {
            return "账户不存在";
        }

        password = PasswordEncryptionUtils.encryption(password);//加密
        String ip = IpOrAddress.getIp();//登录ip
        String address = IpOrAddress.getAddress();//登录地址
        //2、用户使输入密码是否正确
        if (!user.getPassword().equals(password)) {
            String s = loginErrorCache.get(account);
            //第一次访问错误
            if (s == null) {
                logger.error("第1次输错密码");
                loginErrorCache.PUT(account, "1");
            } else {
                int nums = Integer.parseInt(s) + 1;
                loginErrorCache.PUT(account, String.valueOf(nums));
                logger.error("第{}次输错密码", nums);

                //错误次数高
                if (nums >= 5) {
                    Date date = new Date();
                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd :HH:mm:ss");

                    String id = UUID.fastUUID().toString();
                    EmailBody emailBody = new EmailBody("1", id, user.getEmail(), user.getUserName(), dateFormat.format(date), ip, address, null);
                    messageRocketProducer.sendMessage(emailBody);//发送邮件提醒用户


                    LambdaUpdateWrapper<User> updateStatus = new LambdaUpdateWrapper<>();
                    updateStatus.eq(User::getId, user.getId());
                    updateStatus.set(User::getStatus, "2");

                    //更新用户状态
                    userLoginMapper.update(null, updateStatus);
                    login.remove(account);
                }
            }
            logger.error("CA002:{}", "账户或密码错误");
            return "账户或密码错误";
        }


        //3、登录成功
        //更新登录信息(登录地址和时间)
        userLogService.updateLoginAddressAndIp(user);

        //4、记录登录日志
        userLogService.addLoginLog(user);

        // 5.设置token
        // 5.保存用户信息到 redis中
        logger.info("user:{}", user);
        String token = createToken(user);


        // 6.返回token
        return token;
    }


    /**
     * 电子邮件快速登录
     *
     * @param mail        邮件号
     * @param mailCaptcha 邮件验证码
     * @return {@link String}
     */
    @Transactional
    @Override
    public String emailQuickLogin(String mail, String mailCaptcha) {
        // 1.取出redis中验证码
        String codeCache = jetCache.get(mail);
        System.out.println(codeCache);

        // 2.验证码是否过期？ 是否正确？
        if (mailCaptcha == null || !mailCaptcha.equals(codeCache)) {
            // 不一致，报错
            return VERIFICATION_CODE_ERROR;
        }
        // 清除缓存
        jetCache.remove(mail);

        // 3.一致，根据邮箱号查询用户
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getEmail, mail);
        User user = userLoginMapper.selectOne(lqw);


        // 4.判断用户是否存在
        if (user == null) {
            // 不存在，创建新用户
            user = createUserWithEmail(mail);
        }
        if(user.getStatus().equals("2")) {
            LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
            wrapper.set(User::getStatus, "0");
            wrapper.eq(User::getId, user.getId());
            userLoginMapper.update(null,wrapper);
        }

        // 5.登录成功
        //更新登录信息(登录地址和时间)
        userLogService.updateLoginAddressAndIp(user);

        // 6、记录登录日志
        userLogService.addLoginLog(user);

        // 7.设置token
        // 7.保存用户信息到 redis中
        String token = createToken(user);

        // 8.返回token
        return token;
    }

    /**
     * 通过（邮箱/手机号）创建用户
     *
     * @param account 账户
     * @return {@link User}
     */
    private User createUserWithEmail(String account) {
        SnowFlake snowFlake = new SnowFlake(11, 1);
        long id = snowFlake.nextId();
        // 1.创建用户
        User user = new User();
        Membership membership = new Membership();

        // id(雪花算法生成)
        user.setId(id);
        membership.setId(id);

        int accountType = AccountTypeCheckUtils.accountType(account);
        if (accountType == 0) {
            user.setPhone(account);
        } else if (accountType == 1) {
            user.setEmail(account);
        }

        String name = USER_NICK_NAME_PREFIX + RandomUtil.randomString(10);
        user.setUserName(name);
        membership.setUserName(name);

        //上线时切记打开！！！！！！
        String password = UUID.randomUUID().toString();
        password = PasswordEncryptionUtils.encryption(password);//加密
        user.setPassword(password);

        // 2.保存用户信息
        userLoginMapper.insert(user);

        // 3.保存会员信息
        membershipService.addMembership(membership);

        logger.info("新用户{}", user);
        return user;
    }

    /**
     * 创建令牌
     *
     * @param user 用户
     * @return {@link String}
     */
    private String createToken(User user) {
        // 1.1.随机生成token，作为登录令牌
        String token = IdUtils.fastSimpleUUID();
        // 1.2.将User对象转为HashMap存储
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("id", String.valueOf(user.getId()));
        userMap.put("mickName", user.getUserName());
        userMap.put("icon", user.getAvatar());
        userMap.put("type", user.getType());

        // 1.3存储
        String tokenKey = LOGIN_USER_KEY + token;
        StringRedisTemplate.opsForHash().putAll(tokenKey, userMap);

        // 1.4.设置token有效期
        StringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
        return token;
    }

    /**
     * 手机快速登录
     *
     * @param phoneCode    电话号码
     * @param phoneCaptcha 手机验证码
     * @return {@link String}
     */
    @Override
    public String PhoneQuickLogin(String phoneCode, String phoneCaptcha) {
        return null;
    }


}
